package lapsnake.model.dnd;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Dimension;
import java.util.ArrayList;

import javax.swing.Box;
import javax.swing.JPanel;

import owg.util.awt.VerticalLayout;
import lapsnake.model.LapNode;
import lapsnake.model.LapStatement;
import lapsnake.model.NodeRegister;
import lapsnake.model.PyAST;
import lapsnake.model.data.ChildField;
import lapsnake.model.data.ChildList;
import lapsnake.model.data.ChildListener;
import lapsnake.ui.BlendComposite;

public class StmtPanel extends JPanel implements ChildListener<LapNode>
{	
	private static final int Y_OFFSET = 0;
	private static final long serialVersionUID = -5418928937597399838L;
	
	
	JigMover mover;
	
	private ChildList<? extends LapStatement> body;
	private ArrayList<NodePanel<? extends LapStatement>> uis;
	/**All node panels currently in the tree*/
	ArrayList<NodePanel<?>> allPanels;
	
	
	public StmtPanel(PyAST pyAST, ChildList<? extends LapStatement> body)
	{
		super(null);
		this.body = body;
		body.addListener(this);
		setLayout(new VerticalLayout(-ActionNodePanel.MINOR_ICON_DIM, VerticalLayout.LEFT, VerticalLayout.TOP));
		mover = new JigMover(this);
		build();
	}
	
	private void build()
	{
		if(allPanels != null)
		{
			for(NodePanel<?> np : allPanels)
			{
				for(ChildField<?> cf : np.node.getChildFields())
					cf.removeListener(this);
			}
		}
		removeAll();
		uis = new ArrayList<>();
		
		allPanels = new ArrayList<>();
		for(LapStatement n : body)
		{
			@SuppressWarnings("unchecked")
			NodePanel<? extends LapStatement> nui = (NodePanel<? extends LapStatement>) n.getCompactUI(this);
			add(nui);
			allPanels.add(nui);
			nui.getChildPanels(allPanels);
			uis.add(nui);
		}
		for(NodePanel<?> np : allPanels)
		{
			for(ChildField<?> cf : np.node.getChildFields())
				cf.addListener(this);
		}
		Dimension dim = new Dimension(1, ActionNodePanel.MAJOR_ICON_DIM*3);
		add(new Box.Filler(dim, dim, dim));
		updatePreferredSize();
	}
	
	public void updatePreferredSize()
	{
		int minWidth = 0;
		int y = Y_OFFSET+ActionNodePanel.MINOR_ICON_DIM;
		for(int i = 0; i<uis.size(); i++)
		{
			NodePanel<? extends LapStatement> nui = uis.get(i);
			Dimension pref = nui.getPreferredSize();
			minWidth = Math.max(minWidth, pref.width);
			y += pref.height-ActionNodePanel.MINOR_ICON_DIM;
		}
		setPreferredSize(new Dimension(minWidth, y + ActionNodePanel.MAJOR_ICON_DIM));
		if(getParent() != null)
			getParent().revalidate();
		repaint();
	}
	
	@Override
	public void doLayout()
	{
		super.doLayout();
		updatePreferredSize();
	}

	@Override
	public void paint(Graphics g)
	{
		super.paint(g);
		mover.paintComponent(g);
	}
	
	@Override
	protected void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		
		Graphics2D g2 = (Graphics2D)g;
		Color color = NodeRegister.getNodeColor(LapStatement.class);
		g2.setComposite(new BlendComposite(color));
		
		int y = Y_OFFSET+ActionNodePanel.MINOR_ICON_DIM;
		for(int i = 0; i<uis.size(); i++)
		{
			NodePanel<? extends LapStatement> nui = uis.get(i);
			g.drawImage((NodeRegister.getImage(LapStatement.class,"SocketV")), 0, y, null);
			y += nui.getHeight()-ActionNodePanel.MINOR_ICON_DIM;
		}
		g.drawImage((NodeRegister.getImage(LapStatement.class,"SocketV")), 0, y, null);
		
		g2.setComposite(AlphaComposite.SrcOver);

		y = Y_OFFSET+ActionNodePanel.MINOR_ICON_DIM;
		if(y > 0)
		{
			g.setColor(color);
			g.fillRect(0, 0, ActionNodePanel.MINOR_ICON_DIM, y);
			g.setColor(Color.BLACK);
			g.drawLine(ActionNodePanel.MINOR_ICON_DIM, 0, ActionNodePanel.MINOR_ICON_DIM, y-1);
		}
		
		for(int i = 0; i<uis.size(); i++)
		{
			NodePanel<? extends LapStatement> nui = uis.get(i);
			int uy = y + ActionNodePanel.MAJOR_ICON_DIM;
			int dy = y + nui.getHeight()-ActionNodePanel.MINOR_ICON_DIM;
			g.setColor(color);
			g.fillRect(0, uy, ActionNodePanel.MINOR_ICON_DIM, dy-uy);
			g.setColor(Color.BLACK);
			g.drawLine(ActionNodePanel.MINOR_ICON_DIM, uy, ActionNodePanel.MINOR_ICON_DIM, dy-1);
			
			y = dy;
		}
		int uy = y + ActionNodePanel.MAJOR_ICON_DIM;
		g.setColor(color);
		g.fillRect(0, uy, ActionNodePanel.MINOR_ICON_DIM, getHeight()-uy);
		g.setColor(Color.BLACK);
		g.drawLine(ActionNodePanel.MINOR_ICON_DIM, uy, ActionNodePanel.MINOR_ICON_DIM, getHeight()-1);
		
	}
	
	public Socket getSocketAt(Point p)
	{
		int y = Y_OFFSET;
		
		for(int i = 0; i<uis.size(); i++)
		{
			NodePanel<? extends LapStatement> nui = uis.get(i);
			int ny = y+nui.getHeight()-ActionNodePanel.MINOR_ICON_DIM;
			if( (p.y >= y || (i == 0 && p.y >= -nui.getHeight())) && 
				(p.y < ny || (i == uis.size()-1 && p.y < ny+ActionNodePanel.MAJOR_ICON_DIM*2)))
			{
				if (p.x > nui.getWidth()+ActionNodePanel.MINOR_ICON_DIM)
					continue;
				if (p.x > ActionNodePanel.MAJOR_ICON_DIM || p.y >= ny)
				{
					Socket s = nui.getSocketAt(p);
					if(s != null)
						return s;
				}
				else 
					return new Socket(this, null, body, i, false, new Point(0, y));
			}
			y = ny;
		}
		return null;
	}

	/**Called when the user has supplied invalid input on the given component*/
	public void userError(Component editor, Exception err)
	{
		//TODO
		System.err.println(err.getMessage());
	}

	@Override
	public void valueChanged(ChildField<? extends LapNode> field)
	{
		build();
	}
}
